﻿#region Using

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Vk.Methods.Common;

#endregion

namespace Vk.Methods.Board
{
    /// <summary>
    ///     Обсуждения
    /// </summary>
    public class Board
    {
        private readonly Api _api;

        public Board(Api api)
        {
            _api = api;
        }

        /// <summary>
        ///     Добавляет новое сообщение в теме группы.
        /// </summary>
        /// <param name="groupId">ID группы, в обсуждениях которой необходимо создать новое сообщение.</param>
        /// <param name="topicId">ID темы, в которой необходимо оставить новое сообщение.</param>
        /// <param name="text">
        ///     Текст нового сообщения в теме. Параметр является опциональным только если указан параметр
        ///     attachments.
        /// </param>
        /// <param name="attachments">Список объектов, приложенных к сообщению и разделённых символом ",". </param>
        /// <param name="fromGroup">
        ///     true — сообщение будет опубликовано от имени группы, false — сообщение будет опубликовано от
        ///     имени пользователя (по умолчанию).
        /// </param>
        /// <returns>В случае успеха возвращает идентификатор созданного сообщения. </returns>
        public async Task<int> AddComment(int groupId,
                                          int topicId,
                                          string text = null,
                                          IEnumerable<WallAttachment> attachments = null,
                                          bool? fromGroup = null)
        {
            if (text == null & attachments == null)
            {
                throw new ArgumentException(
                    "Невозможно выполнить метод, если не указан хотя бы один с параметров: text, attachments");
            }

            var query = new ApiQuery("board.addComment")
                .Add("group_id", groupId)
                .Add("topic_id", topicId)
                .Add("text", text)
                .Add("from_group", fromGroup)
                .Add("attachments", attachments);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Int().HasValue ? result.Int().Value : -1;
        }

        /// <summary>
        ///     Создает новую тему в списке обсуждений группы.
        /// </summary>
        /// <param name="groupId">ID группы, в обсуждениях которой необходимо создать новую тему.</param>
        /// <param name="title">Заголовок создаваемой темы.</param>
        /// <param name="text">Текст первого сообщения в теме.</param>
        /// <param name="attachments">Список объектов, приложенных к записи и разделённых символом ",".</param>
        /// <param name="fromGroup">
        ///     true — тема будет создана от имени группы, false — тема будет создана от имени пользователя (по
        ///     умолчанию).
        /// </param>
        /// <returns>В случае успеха возвращает идентификатор созданной темы. </returns>
        public async Task<int> AddTopic(int groupId,
                                        string title,
                                        string text,
                                        bool? fromGroup = null,
                                        IEnumerable<WallAttachment> attachments = null)
        {
            if (text == null)
            {
                throw new ArgumentNullException("text");
            }
            if (title == null)
            {
                throw new ArgumentNullException("title");
            }

            var query = new ApiQuery("board.addTopic")
                .Add("group_id", groupId)
                .Add("text", text)
                .Add("title", title)
                .Add("from_group", fromGroup)
                .Add("attachments", attachments);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Int().HasValue ? result.Int().Value : -1;
        }

        /// <summary>
        ///     Закрывает тему в списке обсуждений группы (в такой теме невозможно оставлять новые сообщения).
        /// </summary>
        /// <param name="groupId">ID группы, в обсуждениях которой необходимо закрыть тему.</param>
        /// <param name="topicId">ID темы, которую необходимо закрыть.</param>
        /// <returns>В случае успеха возвращает true. </returns>
        public async Task<bool> CloseTopic(int groupId, int topicId)
        {
            var query = new ApiQuery("board.closeTopic")
                .Add("group_id", groupId)
                .Add("topic_id", topicId);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

        /// <summary>
        ///     Удаляет сообщение темы в обсуждениях группы.
        /// </summary>
        /// <param name="groupId">ID группы, в обсуждениях которой необходимо удалить сообщение.</param>
        /// <param name="topicId">ID темы, которой принадлежит удаляемое сообщение</param>
        /// <param name="commentId">ID удаляемого сообщения</param>
        /// <returns>В случае успеха возвращает true. </returns>
        public async Task<bool> DeleteComment(int groupId, int topicId, int commentId)
        {
            var query = new ApiQuery("board.deleteComment")
                .Add("group_id", groupId)
                .Add("comment_id", commentId)
                .Add("topic_id", topicId);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

        /// <summary>
        ///     Удаляет тему в обсуждениях группы.
        /// </summary>
        /// <param name="groupId">ID группы, в обсуждениях которой необходимо удалить тему.</param>
        /// <param name="topicId">ID удаляемой темы</param>
        /// <returns>В случае успеха возвращает true. </returns>
        public async Task<bool> DeleteTopic(int groupId, int topicId)
        {
            var query = new ApiQuery("board.deleteTopic")
                .Add("group_id", groupId)
                .Add("topic_id", topicId);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

        /// <summary>
        ///     Редактирует одно из сообщений в теме группы.
        /// </summary>
        /// <param name="groupId">ID группы, в обсуждениях которой необходимо изменить сообщение.</param>
        /// <param name="topicId">ID темы, в которой необходимо изменить сообщение.</param>
        /// <param name="commentId">ID сообщения, которое необходимо изменить.</param>
        /// <param name="message">Новый текст сообщения. Параметр является опциональным только если указан параметр attachments.</param>
        /// <param name="attachments">Список объектов, приложенных к сообщению и разделённых символом ",".</param>
        /// <returns>В случае успеха возвращает true. </returns>
        public async Task<bool> EditComment(int groupId,
                                            int topicId,
                                            int commentId,
                                            string message = null,
                                            IEnumerable<string> attachments = null)
        {
            if (message == null & attachments == null)
            {
                throw new ArgumentException(
                    "Невозможно выполнить метод, если не указан хотя бы один с параметров: text, attachments");
            }

            var query = new ApiQuery("board.editComment")
                .Add("group_id", groupId)
                .Add("commment_id", commentId)
                .Add("topic_id", topicId)
                .Add("message", message)
                .Add("attachments", attachments);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

        /// <summary>
        ///     Закрепляет тему в списке обсуждений группы (такая тема при любой сортировке выводится выше остальных).
        /// </summary>
        /// <param name="groupId">ID группы, в обсуждениях которой необходимо закрыть тему.</param>
        /// <param name="topicId">ID темы, которую необходимо закрыть.</param>
        /// <returns>В случае успеха возвращает true. </returns>
        public async Task<bool> FixTopic(int groupId, int topicId)
        {
            var query = new ApiQuery("board.fixTopic")
                .Add("group_id", groupId)
                .Add("topic_id", topicId);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

        /// <summary>
        ///     Возвращает список сообщений в указанной теме.
        /// </summary>
        /// <param name="groupId">ID группы, к обсуждениям которой относится указанная тема.</param>
        /// <param name="topicId">ID темы в группе</param>
        /// <param name="extended">
        ///     Если указать в качестве этого параметра 1, то будет возвращена информация о пользователях,
        ///     являющихся авторами сообщений. По умолчанию 0.
        /// </param>
        /// <param name="offset">Смещение, необходимое для выборки определенного подмножества сообщений.</param>
        /// <param name="count">Количество сообщений, которое необходимо получить (но не более 100). По умолчанию 20.</param>
        /// <param name="needLikes">true — будет возвращено дополнительное поле likes. По умолчанию поле likes не возвращается. </param>
        /// <param name="sort">
        ///     порядок сортировки комментариев:
        ///     asc — хронологический
        ///     desc — антихронологический
        /// </param>
        /// <param name="startCommentId">Идентификатор комментария, начиная с которого нужно вернуть список </param>
        public async Task<TopicCommentsInfo> GetComments(int groupId,
                                                         int topicId,
                                                         bool? extended = null,
                                                         int? offset = null,
                                                         int? count = null,
                                                         bool? needLikes = null,
                                                         SortOrder sort = null,
                                                         int? startCommentId = null)
        {
            var query = new ApiQuery("board.getComments")
                .Add("group_id", groupId)
                .Add("topic_id", topicId)
                .Add("extended", extended)
                .Add("offset", offset)
                .Add("count", count)
                .Add("need_likes", needLikes)
                .Add("start_comment_id", startCommentId)
                .Add("sort", sort?.StringValue);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed ? new TopicCommentsInfo(result) : null;
        }

        /// <summary>
        ///     Возвращает список тем в обсуждениях указанной группы.
        /// </summary>
        /// <param name="groupId">ID группы, список тем которой необходимо получить.</param>
        /// <param name="topicIds">
        ///     Список идентификаторов тем, которые необходимо получить (не более 100). По умолчанию возвращаются
        ///     все темы. Если указан данный параметр, игнорируются параметры order, offset и count (возвращаются все запрошенные
        ///     темы в указанном порядке).
        /// </param>
        /// <param name="order">
        ///     Порядок, в котором необходимо вернуть список тем. Возможные значения:
        ///     1 - по убыванию даты обновления,
        ///     2 - по убыванию даты создания,
        ///     -1 - по возрастанию даты обновления,
        ///     -2 - по возрастанию даты создания.
        ///     По умолчанию темы возвращаются в порядке, установленном администратором группы. "Прилепленные" темы при любой
        ///     сортировке возвращаются первыми в списке.
        /// </param>
        /// <param name="extended">
        ///     Если указать в качестве этого параметра 1, то будет возвращена информация о пользователях,
        ///     являющихся создателями тем или оставившими в них последнее сообщение. По умолчанию 0.
        /// </param>
        /// <param name="offset">Смещение, необходимое для выборки определенного подмножества тем.</param>
        /// <param name="count">Количество тем, которое необходимо получить (но не более 100). По умолчанию 40.</param>
        /// <param name="preview">
        ///     Набор флагов, определяющий, необходимо ли вернуть вместе с информацией о темах текст первых и последних сообщений в
        ///     них. Является суммой флагов:
        ///     1 - вернуть первое сообщение в каждой теме (поле first_comment),
        ///     2 - вернуть последнее сообщение в каждой теме (поле last_comment). По умолчанию 0 (не возвращать текст сообщений).
        /// </param>
        /// <param name="previewLength">
        ///     Количество символов, по которому нужно обрезать первое и последнее сообщение. Укажите 0,
        ///     если Вы не хотите обрезать сообщение. (по умолчанию 90).
        /// </param>
        public async Task<TopicsInfo> GetTopics(int groupId,
                                                IEnumerable<int> topicIds = null,
                                                TopicsSortOrder order = null,
                                                bool? extended = null,
                                                int? offset = null,
                                                int? count = null,
                                                ReturnPreviewFlags preview = null,
                                                int? previewLength = null)
        {
            var query = new ApiQuery("board.getTopics")
                .Add("group_id", groupId)
                .Add("topic_ids", topicIds)
                .Add("order", order?.Value)
                .Add("preview", preview?.Value)
                .Add("extended", extended)
                .Add("offset", offset)
                .Add("count", count)
                .Add("preview_length", previewLength);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed ? new TopicsInfo(result) : null;
        }

        /// <summary>
        ///     Открывает ранее закрытую тему (в ней станет возможно оставлять новые сообщения).
        /// </summary>
        /// <param name="groupId">Идентификатор сообщества, в котором размещено обсуждение.</param>
        /// <param name="topicId">Идентификатор обсуждения.</param>
        /// <returns>После успешного выполнения возвращает true.</returns>
        public async Task<bool> OpenTopic(int groupId, int topicId)
        {
            var query = new ApiQuery("board.openTopic")
                .Add("group_id", groupId)
                .Add("topic_id", topicId);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

        /// <summary>
        ///     Восстанавливает удаленное сообщение темы в обсуждениях группы.
        /// </summary>
        /// <param name="groupId">ID группы, в обсуждениях которой необходимо восстановить сообщение.</param>
        /// <param name="topicId">ID темы, которой принадлежало удаленное сообщение</param>
        /// <param name="commentId">ID удаленного сообщения</param>
        /// <returns>В случае успеха возвращает true</returns>
        public async Task<bool> RestoreComment(int groupId, int topicId, int commentId)
        {
            var query = new ApiQuery("board.restoreComment")
                .Add("group_id", groupId)
                .Add("comment_id", commentId)
                .Add("topic_id", topicId);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }

        /// <summary>
        ///     Отменяет прикрепление темы в списке обсуждений группы (тема будет выводиться согласно выбранной сортировке).
        /// </summary>
        /// <param name="groupId">ID группы, в обсуждениях которой необходимо закрыть тему.</param>
        /// <param name="topicId">ID темы, которую необходимо закрыть.</param>
        /// <returns>В случае успеха возвращает true. </returns>
        public async Task<bool> UnfixTopic(int groupId, int topicId)
        {
            var query = new ApiQuery("board.unfixTopic")
                .Add("group_id", groupId)
                .Add("topic_id", topicId);

            var result = await _api.Manager.Execute(query);

            return _api.Manager.MethodSuccessed && result.Bool().Value;
        }
    }
}